package cipher.io;

import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.FileChannel;

import cipher.util.CipherConst;
import cipher.util.IFCrypt;

public class CipherFileOutputStream extends OutputStream {
	private FileOutputStream fo;
	private IFCrypt cipher;
	private byte [] dbuf =null;
	private int bufpos=0;
	private FileChannel channel;
	private CipherFile file;
	
	public CipherFileOutputStream(CipherFile file){
		cipher = file.getCipher();
		this.file = file;
		dbuf = new byte[CipherConst.kBufferSize];
		try {
			fo = new FileOutputStream(file.getPhysicalFile());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void write(int b) throws IOException {
		if(bufpos == CipherConst.kBufferSize){
			writeByte(null, 0, 0);
		}
		dbuf[bufpos++]=(byte)b;
	}
	
	@Override
	public void write(byte []buf) throws IOException {
		writeByte(buf,0,buf.length);
	}
	
	@Override
	public void write(byte []buf,int offset ,int len) throws IOException {
		writeByte(buf, offset, len);
	}
	private void writeByte(byte []buf,int offset ,int len) throws IOException {
		int bpos=0;
		while(true){
			int cc = CipherConst.kBufferSize - bufpos;
			if(cc > 0){
				cc = len > cc ? cc : len;
				System.arraycopy(buf, offset+bpos, dbuf, bufpos, cc);
				bufpos+=cc;
				len -=cc;
				bpos+=cc;
			}
			if(CipherConst.kBufferSize == bufpos){
				byte []p = cipher.encrypt(dbuf, 0, bufpos);
				fo.write(p,0,p.length);
				bufpos= 0;
			}
			if(len ==0){
				break;
			}
		}
	}
	@Override
	public void flush()throws IOException{
		if(bufpos==CipherConst.kBufferSize){
			byte []p = cipher.encrypt(dbuf, 0, bufpos);
			fo.write(p,0,p.length);
			bufpos= 0;
			fo.flush();
		}
	}
	
	@Override
	public void close() throws IOException{
		if(bufpos > 0){
			byte []p = cipher.encrypt(dbuf, 0, bufpos);
			this.fo.write(p,0,p.length);
		}
		this.fo.close();
		this.file.close();
	}
	
	public FileChannel getChannel() throws IOException{
		if(channel==null)
				channel = new CipherFileChannel(fo.getChannel(), file,true,false);
		return channel;
	}
	public final FileDescriptor getFD() throws IOException{
		return fo.getFD();
		
	}
	protected void finalize() throws Throwable {
	    
	}
}
